
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "utility.h"
#include "qr-common.h"
#include "bit-stream.h"
#include "qr-element.h"

static BitStream* qr_element_encode_mode_num(QRElement* qr_element, int version);
static BitStream* qr_element_encode_mode_alphanum(QRElement* qr_element, int version);
static BitStream* qr_element_encode_mode_8bit(QRElement* qr_element, int version);
static BitStream* qr_element_encode_mode_kanji(QRElement* qr_element, int version);

QRElement* qr_element_new(QRMode mode, unsigned char* source, int size)
{
	QRElement* qr_element;

	// TODO: Add advanced arguments checking.
	if(qr_check_mode(mode) < 0 || NULL == source || size <= 0)
	{
		RES_NULL("Argument is invalid.");
	}

	qr_element = (QRElement*)malloc(sizeof(QRElement));
	if(NULL == qr_element)
	{
		RES_NULL("Invoke malloc error.");
	}
	qr_element->_mode = mode;
	qr_element->_source = (unsigned char*)malloc(size * sizeof(unsigned char));
	if(NULL == qr_element->_source)
	{
		free(qr_element);
		RES_NULL("Invoke malloc error.");
	}
	memcpy(qr_element->_source, source, size * sizeof(unsigned char));
	qr_element->_size = size;
	qr_element->_next = NULL;
	return qr_element;
}

void qr_element_destroy(QRElement* qr_element)
{
	if(qr_element != NULL)
	{
		if(qr_element->_source != NULL)
		{
			free(qr_element->_source);
		}
		free(qr_element);
	}
}

BitStream* qr_element_encode(QRElement* qr_element, int version)
{
	BitStream* result;

	if(NULL == qr_element)
	{
		RES_NULL("Argument is invalid.");
	}
	if(qr_check_mode(qr_element->_mode) < 0 || NULL == qr_element->_source || qr_element->_size <= 0)
	{
		RES_NULL("Arugment is invalid.");
	}

	result = NULL;
	switch(qr_element->_mode)
	{
		case QR_MODE_NUM:
			{
				result = qr_element_encode_mode_num(qr_element, version);
			}
			break;
		case QR_MODE_ALPHANUM:
			{
				result = qr_element_encode_mode_alphanum(qr_element, version);
			}
			break;
		case QR_MODE_8BIT:
			{
				result = qr_element_encode_mode_8bit(qr_element, version);
			}
			break;
		case QR_MODE_KANJI:
			{
				result = qr_element_encode_mode_kanji(qr_element, version);
			}
			break;
		case QR_MODE_INVALID:
		default:
			{
				assert(FALSE);
			}
			break;
	}
	return result;
}


BitStream* qr_element_encode_mode_num(QRElement* qr_element, int version)
{
	BitStream* result;
	int character_count_length;
	int blocks;
	int i;
	unsigned int value;

	if(NULL == qr_element || qr_check_version(version) < 0)
	{
		RES_NULL("Argument is invalid.");
	}
	if(qr_element->_mode != QR_MODE_NUM || NULL == qr_element->_source || qr_element->_size <= 0)
	{
		RES_NULL("Arugment is invalid.");
	}

	result = bit_stream_new();
	if(NULL == result)
	{
		RES_NULL("Invoke bit_stream_new error.");
	}
	if(bit_stream_append_num(result, MODE_INDICATOR_LENGTH, MODE_NUM_INDICATOR) < 0)
	{
		bit_stream_destroy(result);
		RES_NULL("Invoke bit_stream_append_num error.");
	}
	character_count_length = qr_character_count_indicator(version, QR_MODE_NUM);
	if(character_count_length < 0)
	{
		bit_stream_destroy(result);
		RES_NULL("Invoke qr_character_count_indicator error.");
	}
	if(bit_stream_append_num(result, character_count_length, qr_element->_size) < 0)
	{
		bit_stream_destroy(result);
		RES_NULL("Invoke bit_stream_append_num error.");
	}
	blocks = qr_element->_size / 3;
	for(i = 0; i < blocks; ++i)
	{
		value = (unsigned int)(qr_element->_source[i * 3] - '0') * 100;
		value += (unsigned int)(qr_element->_source[i * 3 + 1] - '0') * 10;
		value += (unsigned int)(qr_element->_source[i * 3 + 2] - '0');
		if(bit_stream_append_num(result, 10, value) < 0)
		{
			bit_stream_destroy(result);
			RES_NULL("Invoke bit_stream_append_num error.");
		}
	}
	switch(qr_element->_size - blocks * 3)
	{
		case 0:
			break;
		case 1:
			{
				value = (unsigned int)(qr_element->_source[qr_element->_size - 1] - '0');
				if(bit_stream_append_num(result, 4, value) < 0)
				{
					bit_stream_destroy(result);
					RES_NULL("Invoke bit_stream_append_num error.");
				}

			}
			break;
		case 2:
			{
				value = (unsigned int)(qr_element->_source[qr_element->_size - 2] - '0') * 10;
				value += (unsigned int)(qr_element->_source[qr_element->_size - 1] - '0');
				if(bit_stream_append_num(result, 7, value) < 0)
				{
					bit_stream_destroy(result);
					RES_NULL("Invoke bit_stream_append_num error.");
				}
			}
			break;
		default:
			{
				assert(FALSE);
			}
			break;
	}
	return result;
}

BitStream* qr_element_encode_mode_alphanum(QRElement* qr_element, int version)
{
	RES_NULL("Encoding mode ALPHANUM is not supporting.");
}

BitStream* qr_element_encode_mode_8bit(QRElement* qr_element, int version)
{
	RES_NULL("Encoding mode 8BIT is not supporting.");
}

BitStream* qr_element_encode_mode_kanji(QRElement* qr_element, int version)
{
	RES_NULL("Encoding mode KANJI is not supporting.");
}

